package autosession

import (
	"context"
	"fmt"
	"net/http"
)

// sessionInfo stores a session tracked by the registry.
type sessionInfo struct {
	s *Session
	e error
}

// contextKey is the type used to store the registry in the context.
type contextKey int

// registryKey is the key used to store the registry in the context.
const registryKey contextKey = 0

// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
	var ctx = r.Context()
	registry := ctx.Value(registryKey)
	if registry != nil {
		return registry.(*Registry)
	}
	newRegistry := &Registry{
		request:  r,
		sessions: make(map[string]sessionInfo),
	}
	*r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry))
	return newRegistry
}

type Registry struct {
	request  *http.Request
	sessions map[string]sessionInfo
}

func (s *Registry) Get(name string) (session *Session, err error) {
	if info, ok := s.sessions[name]; ok {
		return info.s, info.e
	}
	return nil, &NotFoundError{"registry not found session: " + name}
}

func (s *Registry) Set(name string, session *Session) (err error) {
	s.sessions[name] = sessionInfo{s: session, e: nil}
	return nil
}

func (s *Registry) Save(ctx context.Context, writter WritterFunc) error {
	errs := []error{}
	for name, info := range s.sessions {
		session := info.s
		if session.store == nil {
			errs = append(errs, fmt.Errorf("sessions: missing store for session %s", name))
		} else if err := session.Save(ctx, writter); err != nil {
			errs = append(errs, fmt.Errorf("error saving session %q -- %v", name, err))
		}
	}
	if len(errs) > 0 {
		var err error
		for _, e := range errs {
			err = fmt.Errorf("%w", e)
		}
		return err
	}
	return nil
}

func Save(ctx context.Context, r *http.Request, writter WritterFunc) error {
	return GetRegistry(r).Save(ctx, writter)
}
